﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;
using Microsoft.WindowsAzure;
using Microsoft.WindowsAzure.ServiceRuntime;
using Microsoft.WindowsAzure.StorageClient;
using System.Net;
using ServiceMessages.ResponsePOCOs;
using ServiceMessages.RequestPOCOs;
using System.IO;

namespace WCFServiceWebRole1
{
    // NOTE: You can use the "Rename" command on the "Refactor" menu to change the class name "Service1" in code, svc and config file together.
    public class Convo : IConvo
    {

        public Convo()
        {
            // Set the maximum number of concurrent connections 
            ServicePointManager.DefaultConnectionLimit = 12;

            // read storage account configuration settings
            CloudStorageAccount.SetConfigurationSettingPublisher((configName, configSetter) =>
            {
                configSetter(RoleEnvironment.GetConfigurationSettingValue(configName));
            });

            var storageAccount = CloudStorageAccount.FromConfigurationSetting("DataConnectionString");
        }

        public LoginResponse loginUser(string email, string password)
        {
            using (var convoContext = new Convo498Entities())
            {
                var user = convoContext.Users.Single(u => u.Email.Equals(email) && u.Password.Equals(password));

                return new LoginResponse { ID = user.ID, FirstName = user.FirstName, LastName = user.LastName, Email = user.Email, Birthdate = user.Birthdate.Ticks, Status = user.Status };
            }
        }

        public LoginResponse registerUser(RegistrationRequest r)
        {
            using (var convoContext = new Convo498Entities())
            {
                var user = new User {FirstName = r.FirstName, LastName = r.LastName, Email = r.Email, Password = r.Password, Birthdate = new DateTime(r.Birthdate) };
                convoContext.Users.Add(user);
                convoContext.SaveChanges();

                var loggedInUser = convoContext.Users.Single(u => u.Email.Equals(r.Email) && u.Password.Equals(r.Password));

                return new LoginResponse { ID = user.ID, FirstName = user.FirstName, LastName = user.LastName, Email = user.Email, Birthdate = user.Birthdate.Ticks, Status = user.Status };
            }
        }

        public LoginResponse editProfile(EditProfileRequest e)
        {
            using (var convoContext = new Convo498Entities())
            {
                var user = convoContext.Users.Where(u => e.UserId == u.ID).SingleOrDefault();

                if (user != null)
                {
                    user.FirstName = e.FirstName;
                    user.LastName = e.LastName;
                    user.Email = e.Email;
                    user.Birthdate = new DateTime(e.Birthdate);

                    convoContext.SaveChanges();

                    return new LoginResponse { ID = user.ID, FirstName = user.FirstName, LastName = user.LastName, Email = user.Email, Birthdate = user.Birthdate.Ticks };
                }
                return null;
            }
        }

        public MessageResponse requestFriend(int requesterId, int requesteeId)
        {
            using (var convoContext = new Convo498Entities())
            {
                convoContext.Friends.Add(new Friend { RequesterID = requesterId, RequesteeID = requesteeId, IsAccepted = true });
                convoContext.SaveChanges();

                return new MessageResponse { Response = "Friend request made." };
            }
        }

        public MessageResponse acceptFriend(int accepterId, int accepteeId)
        {
            using (var convoContext = new Convo498Entities())
            {
                var friendRequest = convoContext.Friends.Where(f => accepterId == f.RequesteeID && accepteeId == f.RequesterID).SingleOrDefault();
                friendRequest.IsAccepted = true;
                convoContext.SaveChanges();

                return new MessageResponse { Response = "Friend request accepted." };
            }
        }

        public List<FriendResponse> getFriends(int userId)
        {
            using (var convoContext = new Convo498Entities())
            {
                var friends = convoContext.Friends.Where(f => f.RequesteeID == userId && f.IsAccepted == true).Select(f => new FriendResponse { Id = f.User.ID, FirstName = f.User.FirstName, LastName = f.User.LastName, Status = f.User.Status, BirthDate = f.User.Birthdate }).ToList();
                friends.AddRange(convoContext.Friends.Where(f => f.RequesterID == userId && f.IsAccepted == true).Select(f => new FriendResponse { Id = f.User1.ID, FirstName = f.User1.FirstName, LastName = f.User1.LastName, Status = f.User.Status, BirthDate = f.User.Birthdate }));
                return friends;
            }
        }

        public MessageResponse removeFriend(RemoveFriendRequest r)
        {
            using (var convoContext = new Convo498Entities())
            {
                var friendEntries = convoContext.Friends.Where(f => r.FriendIds.Contains(f.RequesteeID) && r.UserId == f.RequesterID).ToList();
                friendEntries.AddRange(convoContext.Friends.Where(f => r.FriendIds.Contains(f.RequesterID) && r.UserId == f.RequesteeID));
                foreach (Friend f in friendEntries)
                {
                    convoContext.Friends.Remove(f);
                }
                convoContext.SaveChanges();
                return new MessageResponse { Response = "Successfully removed friend." };
            }
        }

        public List<GroupResponse> getGroups(int userId)
        {
            using (var convoContext = new Convo498Entities())
            {
                var groups = convoContext.Groups.Where(g => g.GroupMembers.Where(m => userId == m.UserID).FirstOrDefault() != null).Select(g => new GroupResponse { Id = g.ID, Name = g.Name }).ToList();
                return groups;
            }
        }

        public MessageResponse leaveGroup(LeaveGroupRequest l)
        {
            using (var convoContext = new Convo498Entities())
            {
                var groupMemberEntries = convoContext.GroupMembers.Where(gm => l.GroupIds.Contains(gm.GroupID) && l.UserId == gm.UserID).ToList();
                foreach (GroupMember gm in groupMemberEntries)
                {
                    var group = gm.Group;
                    if (group.GroupMembers.Count == 2)
                    { // delete group if removing 2nd to last member (can't group chat with only you...)
                        convoContext.Groups.Remove(group);
                    }
                    convoContext.GroupMembers.Remove(gm);
                } 
                convoContext.SaveChanges();
                return new MessageResponse { Response = "Successfully removed user from group." };
            }
        }

        public SetupChatResponse getGroupChat(int groupId)
        {
            using (var convoContext = new Convo498Entities())
            {
                var chat = new SetupChatResponse { Endpoints = convoContext.GroupMembers.Where(gm => groupId == gm.GroupID).Select(gm => gm.User.Endpoint).ToList() };
                
                return chat;
            }
        }

        public SetupChatResponse getChat(int userId)
        {
            using (var convoContext = new Convo498Entities())
            {
                var chat = new SetupChatResponse { Endpoints = convoContext.Users.Where(u => userId == u.ID).Select(u => u.Endpoint).ToList()};

                return chat;
            }
        }

        public List<FriendResponse> searchUsers(SearchUsersRequest s)
        {
            using (var convoContext = new Convo498Entities())
            {
                var search = s.SearchText.Split(' ');
                if (search.Length > 1)
                {
                    var firstName = search[0];
                    var lastName = search[1];
                    var users = convoContext.Users.Where(u => u.FirstName.Contains(firstName) || u.LastName.Contains(lastName)).Select(u => new FriendResponse { Id = u.ID, FirstName = u.FirstName, LastName = u.LastName });
                    users = users.OrderBy(u => u.FirstName.CompareTo(firstName) + u.LastName.CompareTo(lastName));

                    return users.ToList();
                }
                else
                {
                    var name = search[0];

                    var users = convoContext.Users.Where(u => u.FirstName.Contains(name) || u.LastName.Contains(name)).Select(u => new FriendResponse { Id = u.ID, FirstName = u.FirstName, LastName = u.LastName });
                    users = users.OrderBy(u => u.FirstName.CompareTo(name) + u.LastName.CompareTo(name));

                    return users.ToList();
                }
            }
        }

        public MessageResponse updateEndpoint(int userId, string endpoint)
        {
            using (var convoContext = new Convo498Entities())
            {
                var user = convoContext.Users.Where(u => userId == u.ID).SingleOrDefault();
                if (user != null)
                {
                    user.Endpoint = endpoint;
                    convoContext.SaveChanges();

                    return new MessageResponse { Response = "Successfully updated user's endpoint." };
                }

                return new MessageResponse { Response = "Failed to update user's endpoint: user not found." };
            }
        }

        public MessageResponse updateStatus(int userId, string status)
        {
            using (var convoContext = new Convo498Entities())
            {
                var user = convoContext.Users.Where(u => userId == u.ID).SingleOrDefault();
                user.Status = status;
                convoContext.SaveChanges();

                return new MessageResponse { Response = "Successfully updated user's status." };
            }
        }

        public MessageResponse updateLocation(int userId, double latitude, double longitude)
        {
            using (var convoContext = new Convo498Entities())
            {
                var user = convoContext.Users.Where(u => userId == u.ID).SingleOrDefault();
                if (user != null)
                {
                    user.Latitude = latitude;
                    user.Longitude = longitude;
                    user.LocationTime = DateTime.UtcNow;
                    convoContext.SaveChanges();

                    return new MessageResponse { Response = "Successfully updated user's location." };
                }

                return new MessageResponse { Response = "Failed to update user's location: user not found." };
            }
        }

        public List<LocationResponse> getLocations(int userId)
        {
            using (var convoContext = new Convo498Entities())
            {
                DateTime currentTime = DateTime.UtcNow;
                TimeSpan tenMin = new TimeSpan(0, 10, 0);
                var friends = convoContext.Friends.Where(f => f.RequesteeID == userId && f.IsAccepted == true);
                var locations = friends.Where(f => (currentTime - f.User.LocationTime.Value) <= tenMin).Select(f => f.User).Select(u => new LocationResponse { Name = u.FirstName + " " + u.LastName, Latitude = (double)u.Latitude, Longitude = (double)u.Longitude }).ToList();
                
                friends = convoContext.Friends.Where(f => f.RequesterID == userId && f.IsAccepted == true);
                locations.AddRange(friends.Where(f => (currentTime - f.User1.LocationTime.Value) <= tenMin).Select(f => f.User1).Select(u => new LocationResponse { Name = u.FirstName + " " + u.LastName, Latitude = (double)u.Latitude, Longitude = (double)u.Longitude }));
            
                return locations;
            }
        }

        public MessageResponse createGroup(CreateGroupRequest c)
        {
            using (var convoContext = new Convo498Entities())
            {
                var group = new Group { Name = c.Name };
                foreach (int id in c.MemberIds)
                {
                    var member = new GroupMember { UserID = id };
                    group.GroupMembers.Add(member);
                }
                convoContext.Groups.Add(group);
                convoContext.SaveChanges();

                return new MessageResponse { Response = "Successfully created group " + c.Name + "." };
            }
        }

        public MessageResponse saveMessage(SaveMessageRequest s)
        {
            using (var convoContext = new Convo498Entities())
            {
                var message = new Message { SenderID = s.senderId, Content = s.content, GroupID = s.groupId, RecipientID  = s.recipientId, TimeStamp = s.timeStamp };
                convoContext.Messages.Add(message);
                convoContext.SaveChanges();

                return new MessageResponse { Response = "Successfully saved message." };
            }
        }

        public CompositeType GetDataUsingDataContract(CompositeType composite)
        {
            if (composite == null)
            {
                throw new ArgumentNullException("composite");
            }
            if (composite.BoolValue)
            {
                composite.StringValue += "Suffix";
            }
            return composite;
        }
    }
}
